Django + RLS でマルチテナント
実証コード
参考文献
ログインアカウントによって表示されるレコードが異なる
admin(テナント所属なし)
https://scrapbox.io/files/6081c41ae1784300228e757a.png
haru(テナント1)
https://scrapbox.io/files/6081c43e673e0d001c1102ae.png
terada(テナント2)
https://scrapbox.io/files/6081c4fac860320021388029.png
DBにroleを設定したログ
code:console
$ docker-compose exec db psql db -U db
psql (13.2 (Debian 13.2-1.pgdg100+1))
Type "help" for help.
db=# \dt
public | app1_tenantuser | table | db
public | app1_tenantuser_groups | table | db
public | app1_tenantuser_user_permissions | table | db
public | auth_group | table | db
public | auth_group_permissions | table | db
public | auth_permission | table | db
public | customers | table | db
public | django_admin_log | table | db
public | django_content_type | table | db
public | django_migrations | table | db
public | django_session | table | db
public | tenants | table | db
db=# select * from customers;
1 | aa | aa | aa | aa | 2
2 | bb | bb | bb | bb | 1
3 | cc | cc | cc | cc | 2
4 | dd | dd | dd | dd | 1
5 | ee | ee | ee | ee | 2
6 | ff | ff | ff | ff | 1
7 | gg | gg | gg | gg | 1
8 | hh | hh | hh | hh | 2
9 | ii | ii | ii | ii | 1
10 | jj | jj | jj | jj | 2
db=# CREATE ROLE "1";
CREATE ROLE
db=# CREATE ROLE "2";
CREATE ROLE
db=# CREATE ROLE tenantuser;
CREATE ROLE
db=# GRANT select, insert ON customers TO tenantuser;
GRANT
db=# GRANT tenantuser TO "1";
GRANT ROLE
db=# GRANT tenantuser TO "2";
GRANT ROLE
db=# ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
ALTER TABLE
db=# CREATE POLICY tenantuser_customers ON customers USING(tenant_id::text = current_user);
CREATE POLICY
db=# SELECT session_user, current_user;
db | db
db=# SELECT * FROM customers;
1 | aa | aa | aa | aa | 2
2 | bb | bb | bb | bb | 1
3 | cc | cc | cc | cc | 2
4 | dd | dd | dd | dd | 1
5 | ee | ee | ee | ee | 2
6 | ff | ff | ff | ff | 1
7 | gg | gg | gg | gg | 1
8 | hh | hh | hh | hh | 2
9 | ii | ii | ii | ii | 1
10 | jj | jj | jj | jj | 2
db=# SET ROLE "1";
SET
db=> SELECT * FROM customers;
2 | bb | bb | bb | bb | 1
4 | dd | dd | dd | dd | 1
6 | ff | ff | ff | ff | 1
7 | gg | gg | gg | gg | 1
9 | ii | ii | ii | ii | 1
アプリからデータアクセスを許可する(django_admin_log, django_sessionは必須)
code:console
db=# GRANT select, insert, delete ON django_session TO tenantuser;
db=# GRANT select, insert ON django_admin_log TO tenantuser;
db=# GRANT select, insert ON django_content_type TO tenantuser;
db=# GRANT select, insert ON auth_group TO tenantuser;
db=# GRANT select, insert ON auth_permission TO tenantuser;
アプリからデータアクセスを許可する(tenant_usersは必須)
code:console
db=# GRANT select, insert ON tenants TO tenantuser;
db=# GRANT select, insert ON tenant_users TO tenantuser;
db=# GRANT select, insert ON tenant_users_groups TO tenantuser;
db=# GRANT select, insert ON tenant_users_user_permissions TO tenantuser;
所属テナントのデータのみを見せたい場合、各テーブルにRLSを設定
code:console
db=# ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;
db=# CREATE POLICY tenantuser_tenants ON tenants USING(id::text = current_user);
db=# ALTER TABLE app1_tenantuser ENABLE ROW LEVEL SECURITY;
db=# CREATE POLICY tenantuser_tenantusers ON tenant_users USING(tenant_id::text = current_user);